home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / drivers / scsi / scsi_ioctl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  8.4 KB  |  305 lines

  1. #include <asm/segment.h>
  2. #include <asm/system.h>
  3.  
  4. #include <linux/errno.h>
  5. #include <linux/kernel.h>
  6. #include <linux/sched.h>
  7. #include <linux/string.h>
  8.  
  9. #include "../block/blk.h"
  10. #include "scsi.h"
  11. #include "hosts.h"
  12. #include "scsi_ioctl.h"
  13.  
  14. #define MAX_RETRIES 5
  15. #define MAX_TIMEOUT 200
  16. #define MAX_BUF 4096
  17.  
  18. #define max(a,b) (((a) > (b)) ? (a) : (b))
  19.  
  20. /*
  21.  * If we are told to probe a host, we will return 0 if    the host is not
  22.  * present, 1 if the host is present, and will return an identifying
  23.  * string at *arg, if arg is non null, filling to the length stored at
  24.  * (int *) arg
  25.  */
  26.  
  27. static int ioctl_probe(struct Scsi_Host * host, void *buffer)
  28. {
  29.     int temp;
  30.     unsigned int len,slen;
  31.     const char * string;
  32.  
  33.     if ((temp = host->hostt->present) && buffer) {
  34.         len = get_fs_long ((unsigned long *) buffer);
  35.         string = host->hostt->info();
  36.         slen = strlen(string);
  37.         if (len > slen)
  38.             len = slen + 1;
  39.         verify_area(VERIFY_WRITE, buffer, len);
  40.         memcpy_tofs (buffer, string, len);
  41.     }
  42.     return temp;
  43. }
  44.  
  45. /*
  46.  *
  47.  * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
  48.  * The MAX_TIMEOUT and MAX_RETRIES  variables are used.
  49.  *
  50.  * dev is the SCSI device struct ptr, *(int *) arg is the length of the
  51.  * input data, if any, not including the command string & counts,
  52.  * *((int *)arg + 1) is the output buffer size in bytes.
  53.  *
  54.  * *(char *) ((int *) arg)[2] the actual command byte.
  55.  *
  56.  * Note that no more than MAX_BUF data bytes will be transfered.  Since
  57.  * SCSI block device size is 512 bytes, I figured 1K was good.
  58.  * but (WDE) changed it to 8192 to handle large bad track buffers.
  59.  * ERY: I changed this to a dynamic allocation using scsi_malloc - we were
  60.  * getting a kernel stack overflow which was crashing the system when we
  61.  * were using 8192 bytes.
  62.  *
  63.  * This size *does not* include the initial lengths that were passed.
  64.  *
  65.  * The SCSI command is read from the memory location immediately after the
  66.  * length words, and the input data is right after the command.  The SCSI
  67.  * routines know the command size based on the opcode decode.
  68.  *
  69.  * The output area is then filled in starting from the command byte.
  70.  */
  71.  
  72. static void scsi_ioctl_done (Scsi_Cmnd * SCpnt)
  73. {
  74.   struct request * req;
  75.   struct task_struct * p;
  76.  
  77.   req = &SCpnt->request;
  78.   req->dev = 0xfffe; /* Busy, but indicate request done */
  79.  
  80.   if ((p = req->waiting) != NULL) {
  81.     req->waiting = NULL;
  82.     p->state = TASK_RUNNING;
  83.     if (p->counter > current->counter)
  84.       need_resched = 1;
  85.   }
  86. }
  87.  
  88. static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
  89. {
  90.     int result;
  91.     Scsi_Cmnd * SCpnt;
  92.  
  93.     SCpnt = allocate_device(NULL, dev->index, 1);
  94.     scsi_do_cmd(SCpnt,  cmd, NULL,  0,
  95.             scsi_ioctl_done,  MAX_TIMEOUT,
  96.             MAX_RETRIES);
  97.  
  98.     if (SCpnt->request.dev != 0xfffe){
  99.       SCpnt->request.waiting = current;
  100.       current->state = TASK_UNINTERRUPTIBLE;
  101.       while (SCpnt->request.dev != 0xfffe) schedule();
  102.     };
  103.  
  104.     if(driver_byte(SCpnt->result) != 0)
  105.       switch(SCpnt->sense_buffer[2] & 0xf) {
  106.       case ILLEGAL_REQUEST:
  107.         if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0;
  108.         else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
  109.         break;
  110.       case NOT_READY: /* This happens if there is no disc in drive */
  111.         if(dev->removable){
  112.           printk("Device not ready.  Make sure there is a disc in the drive.\n");
  113.           break;
  114.         };
  115.       case UNIT_ATTENTION:
  116.         if (dev->removable){
  117.           dev->changed = 1;
  118.           SCpnt->result = 0; /* This is no longer considered an error */
  119.           printk("Disc change detected.\n");
  120.           break;
  121.         };
  122.       default: /* Fall through for non-removable media */
  123.         printk("SCSI CD error: host %d id %d lun %d return code = %x\n",
  124.            dev->host->host_no,
  125.            dev->id,
  126.            dev->lun,
  127.            SCpnt->result);
  128.         printk("\tSense class %x, sense error %x, extended sense %x\n",
  129.            sense_class(SCpnt->sense_buffer[0]),
  130.            sense_error(SCpnt->sense_buffer[0]),
  131.            SCpnt->sense_buffer[2] & 0xf);
  132.  
  133.       };
  134.  
  135.     result = SCpnt->result;
  136.     SCpnt->request.dev = -1;  /* Mark as not busy */
  137.     wake_up(&scsi_devices[SCpnt->index].device_wait);
  138.     return result;
  139. }
  140.  
  141. static int ioctl_command(Scsi_Device *dev, void *buffer)
  142. {
  143.     char * buf;
  144.     char cmd[12];
  145.     char * cmd_in;
  146.     Scsi_Cmnd * SCpnt;
  147.     unsigned char opcode;
  148.     int inlen, outlen, cmdlen;
  149.     int needed;
  150.     int result;
  151.  
  152.     if (!buffer)
  153.         return -EINVAL;
  154.  
  155.     inlen = get_fs_long((unsigned long *) buffer);
  156.     outlen = get_fs_long( ((unsigned long *) buffer) + 1);
  157.  
  158.     cmd_in = (char *) ( ((int *)buffer) + 2);
  159.     opcode = get_fs_byte(cmd_in);
  160.  
  161.     needed = (inlen > outlen ? inlen : outlen);
  162.     if(needed){
  163.       needed = (needed + 511) & ~511;
  164.       if (needed > MAX_BUF) needed = MAX_BUF;
  165.       buf = (char *) scsi_malloc(needed);
  166.       if (!buf) return -ENOMEM;
  167.     } else
  168.       buf = NULL;
  169.  
  170.     memcpy_fromfs ((void *) cmd,  cmd_in,  cmdlen = COMMAND_SIZE (opcode));
  171.     memcpy_fromfs ((void *) buf,  (void *) (cmd_in + cmdlen), inlen > MAX_BUF ? MAX_BUF : inlen);
  172.  
  173.     cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5);
  174.  
  175. #ifndef DEBUG_NO_CMD
  176.  
  177.     SCpnt = allocate_device(NULL, dev->index, 1);
  178.  
  179.     scsi_do_cmd(SCpnt,  cmd,  buf, needed,  scsi_ioctl_done,  MAX_TIMEOUT, 
  180.             MAX_RETRIES);
  181.  
  182.     if (SCpnt->request.dev != 0xfffe){
  183.       SCpnt->request.waiting = current;
  184.       current->state = TASK_UNINTERRUPTIBLE;
  185.       while (SCpnt->request.dev != 0xfffe) schedule();
  186.     };
  187.  
  188.  
  189.     /* If there was an error condition, pass the info back to the user. */
  190.     if(SCpnt->result) {
  191.       result = verify_area(VERIFY_WRITE, cmd_in, sizeof(SCpnt->sense_buffer));
  192.       if (result)
  193.         return result;
  194.       memcpy_tofs((void *) cmd_in,  SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
  195.     } else {
  196.  
  197.       result = verify_area(VERIFY_WRITE, cmd_in, (outlen > MAX_BUF) ? MAX_BUF  : outlen);
  198.       if (result)
  199.         return result;
  200.       memcpy_tofs ((void *) cmd_in,  buf,  (outlen > MAX_BUF) ? MAX_BUF  : outlen);
  201.     };
  202.     result = SCpnt->result;
  203.     SCpnt->request.dev = -1;  /* Mark as not busy */
  204.     if (buf) scsi_free(buf, needed);
  205.     wake_up(&scsi_devices[SCpnt->index].device_wait);
  206.     return result;
  207. #else
  208.     {
  209.     int i;
  210.     printk("scsi_ioctl : device %d.  command = ", dev->id);
  211.     for (i = 0; i < 12; ++i)
  212.         printk("%02x ", cmd[i]);
  213.     printk("\nbuffer =");
  214.     for (i = 0; i < 20; ++i)
  215.         printk("%02x ", buf[i]);
  216.     printk("\n");
  217.     printk("inlen = %d, outlen = %d, cmdlen = %d\n",
  218.         inlen, outlen, cmdlen);
  219.     printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
  220.     }
  221.     return 0;
  222. #endif
  223. }
  224.  
  225.  
  226.  
  227. /*
  228.     the scsi_ioctl() function differs from most ioctls in that it does
  229.     not take a major/minor number as the dev filed.  Rather, it takes
  230.     a pointer to a scsi_devices[] element, a structure.
  231. */
  232. int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
  233. {
  234.     char scsi_cmd[12];
  235.  
  236.     if ((cmd != 0 && dev->index > NR_SCSI_DEVICES))
  237.         return -ENODEV;
  238.  
  239.     switch (cmd) {
  240.         case SCSI_IOCTL_GET_IDLUN:
  241.             verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
  242.             put_fs_long(dev->id + (dev->lun << 8) +
  243.                     (dev->host->host_no << 16), (unsigned long *) arg);
  244.             return 0;
  245.         case SCSI_IOCTL_TAGGED_ENABLE:
  246.             if(!suser())  return -EACCES;
  247.             if(!dev->tagged_supported) return -EINVAL;
  248.             dev->tagged_queue = 1;
  249.             dev->current_tag = 1;
  250.             break;
  251.         case SCSI_IOCTL_TAGGED_DISABLE:
  252.             if(!suser())  return -EACCES;
  253.             if(!dev->tagged_supported) return -EINVAL;
  254.             dev->tagged_queue = 0;
  255.             dev->current_tag = 0;
  256.             break;
  257.         case SCSI_IOCTL_PROBE_HOST:
  258.             return ioctl_probe(dev->host, arg);
  259.         case SCSI_IOCTL_SEND_COMMAND:
  260.             if(!suser())  return -EACCES;
  261.             return ioctl_command((Scsi_Device *) dev, arg);
  262.         case SCSI_IOCTL_DOORLOCK:
  263.             if (!dev->removable || !dev->lockable) return 0;
  264.             scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
  265.             scsi_cmd[1] = dev->lun << 5;
  266.             scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
  267.             scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
  268.             return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
  269.             break;
  270.         case SCSI_IOCTL_DOORUNLOCK:
  271.             if (!dev->removable || !dev->lockable) return 0;
  272.             scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
  273.             scsi_cmd[1] = dev->lun << 5;
  274.             scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
  275.             scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
  276.             return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
  277.         case SCSI_IOCTL_TEST_UNIT_READY:
  278.             scsi_cmd[0] = TEST_UNIT_READY;
  279.             scsi_cmd[1] = dev->lun << 5;
  280.             scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
  281.             scsi_cmd[4] = 0;
  282.             return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
  283.             break;
  284.         default :
  285.             return -EINVAL;
  286.     }
  287.     return -EINVAL;
  288. }
  289.  
  290. /*
  291.  * Just like scsi_ioctl, only callable from kernel space with no 
  292.  * fs segment fiddling.
  293.  */
  294.  
  295. int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
  296.   unsigned long oldfs;
  297.   int tmp;
  298.   oldfs = get_fs();
  299.   set_fs(get_ds());
  300.   tmp = scsi_ioctl (dev, cmd, arg);
  301.   set_fs(oldfs);
  302.   return tmp;
  303. }
  304.  
  305.